﻿using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Collections.Generic;
using System.IO;

namespace System
{
  [DebuggerStepThrough]
  public static class BitmapExtensions
  {
    public static Bitmap ScaleToSize(this Bitmap bitmap, Size size)
    {
      return bitmap.ScaleToSize(size.Width, size.Height);
    }
    public static Bitmap ScaleToSize(this Bitmap bitmap, int width, int height)
    {
      var scaledBitmap = new Bitmap(width, height);
      using (var g = Graphics.FromImage(scaledBitmap))
      {
        g.InterpolationMode = InterpolationMode.HighQualityBicubic;
        g.DrawImage(bitmap, 0, 0, width, height);
      }
      return scaledBitmap;
    }
    public static Bitmap ScaleProportional(this Bitmap bitmap, Size size)
    {
      return bitmap.ScaleProportional(size.Width, size.Height);
    }
    public static Bitmap ScaleProportional(this Bitmap bitmap, int width, int height)
    {
      float proportionalWidth, proportionalHeight;

      if (width.Equals(0))
      {
        proportionalWidth = ((float)height) / bitmap.Size.Height * bitmap.Width;
        proportionalHeight = height;
      }
      else if (height.Equals(0))
      {
        proportionalWidth = width;
        proportionalHeight = ((float)width) / bitmap.Size.Width * bitmap.Height;
      }
      else if (((float)width) / bitmap.Size.Width * bitmap.Size.Height <= height)
      {
        proportionalWidth = width;
        proportionalHeight = ((float)width) / bitmap.Size.Width * bitmap.Height;
      }
      else
      {
        proportionalWidth = ((float)height) / bitmap.Size.Height * bitmap.Width;
        proportionalHeight = height;
      }

      return bitmap.ScaleToSize((int)proportionalWidth, (int)proportionalHeight);
    }
    public static Bitmap ScaleToSizeProportional(this Bitmap bitmap, Size size)
    {
      return bitmap.ScaleToSizeProportional(Color.White, size);
    }
    public static Bitmap ScaleToSizeProportional(this Bitmap bitmap, Color backgroundColor, Size size)
    {
      return bitmap.ScaleToSizeProportional(backgroundColor, size.Width, size.Height);
    }
    public static Bitmap ScaleToSizeProportional(this Bitmap bitmap, int width, int height)
    {
      return bitmap.ScaleToSizeProportional(Color.White, width, height);
    }
    public static Bitmap ScaleToSizeProportional(this Bitmap bitmap, Color backgroundColor, int width, int height)
    {
      var scaledBitmap = new Bitmap(width, height);
      using (var g = Graphics.FromImage(scaledBitmap))
      {
        g.Clear(backgroundColor);

        var proportionalBitmap = bitmap.ScaleProportional(width, height);

        var imagePosition = new Point((int)((width - proportionalBitmap.Width) / 2m), (int)((height - proportionalBitmap.Height) / 2m));
        g.DrawImage(proportionalBitmap, imagePosition);
      }

      return scaledBitmap;
    }


    /// <summary>
    /// A quick lookup for getting image encoders
    /// </summary>
    private static Dictionary<string, ImageCodecInfo> encoders = null;

    /// <summary>
    /// A quick lookup for getting image encoders
    /// </summary>
    public static Dictionary<string, ImageCodecInfo> Encoders
    {
      //get accessor that creates the dictionary on demand
      get
      {
        //if the quick lookup isn't initialised, initialise it
        if (encoders == null)
        {
          encoders = new Dictionary<string, ImageCodecInfo>();
        }

        //if there are no codecs, try loading them
        if (encoders.Count == 0)
        {
          //get all the codecs
          foreach (ImageCodecInfo codec in ImageCodecInfo.GetImageEncoders())
          {
            //add each codec to the quick lookup
            encoders.Add(codec.MimeType.ToLower(), codec);
          }
        }

        //return the lookup
        return encoders;
      }
    }

    /// <summary>
    /// Resize the image to the specified width and height.
    /// </summary>
    /// <param name="image">The image to resize.</param>
    /// <param name="width">The width to resize to.</param>
    /// <param name="height">The height to resize to.</param>
    /// <returns>The resized image.</returns>
    public static Drawing.Bitmap ResizeImage(Drawing.Image image, int width, int height)
    {
      //a holder for the result
      Bitmap result = new Bitmap(width, height);

      //use a graphics object to draw the resized image into the bitmap
      using (Graphics graphics = Graphics.FromImage(result))
      {
        //set the resize quality modes to high quality
        graphics.CompositingQuality = CompositingQuality.HighQuality;
        graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
        graphics.SmoothingMode = SmoothingMode.HighQuality;
        //draw the image into the target bitmap
        graphics.DrawImage(image, 0, 0, result.Width, result.Height);
      }

      //return the resulting bitmap
      return result;
    }

    /// <summary> 
    /// Saves an image as a jpeg image, with the given quality 
    /// </summary> 
    /// <param name="path">Path to which the image would be saved.</param> 
    /// <param name="quality">An integer from 0 to 100, with 100 being the 
    /// highest quality</param> 
    /// <exception cref="ArgumentOutOfRangeException">
    /// An invalid value was entered for image quality.
    /// </exception>
    public static void SaveJpeg(string path, Image image, int quality)
    {
      //ensure the quality is within the correct range
      if ((quality < 0) || (quality > 100))
      {
        //create the error message
        string error = String.Format("Jpeg image quality must be between 0 and 100, with 100 being the highest quality.  A value of {0} was specified.", quality);
        //throw a helpful exception
        throw new ArgumentOutOfRangeException(error);
      }

      //create an encoder parameter for the image quality
      EncoderParameter qualityParam = new EncoderParameter(Encoder.Quality, quality);
      //get the jpeg codec
      ImageCodecInfo jpegCodec = GetEncoderInfo("image/jpeg");

      //create a collection of all parameters that we will pass to the encoder
      EncoderParameters encoderParams = new EncoderParameters(1);
      //set the quality parameter for the codec
      encoderParams.Param[0] = qualityParam;
      //save the image using the codec and the parameters
      image.Save(path, jpegCodec, encoderParams);
    }

    /// <summary> 
    /// Returns the image codec with the given mime type 
    /// </summary> 
    public static ImageCodecInfo GetEncoderInfo(string mimeType)
    {
      //do a case insensitive search for the mime type
      string lookupKey = mimeType.ToLower();

      //the codec to return, default to null
      ImageCodecInfo foundCodec = null;

      //if we have the encoder, get it to return
      if (Encoders.ContainsKey(lookupKey))
      {
        //pull the codec from the lookup
        foundCodec = Encoders[lookupKey];
      }

      return foundCodec;
    }

    public static string SerializeToString(this Image img)
    {
      var ms = new MemoryStream();
      img.Save(ms, img.RawFormat);
      byte[] array = ms.ToArray();
      return Convert.ToBase64String(array);
    }
    public static Image DeserializeImage(this string imageString)
    {
      if (imageString == null)
        throw new ArgumentNullException("imageString");

      byte[] array = Convert.FromBase64String(imageString);
      Image image = Image.FromStream(new MemoryStream(array));
      return image;
    }

  }
}
